package engine

import (
	"context"
	"errors"
	"fmt"
	"sync"
)

type Engine interface {
	// RegisterForAction can register operator to act map,it can be used by inputs
	RegisterForAction(act string, operator Operator) error
	// Run use an input and flow inputItem to get a result or error
	// you can use context to inject some object you need
	Run(ctx context.Context, in interface{}, inputs []InputItem) (out interface{}, err error)
}

type Operator interface {
	// Process use result of previous action and input parameter to output a result and error
	Process(ctx context.Context, pre, in interface{}) (out interface{}, err error)
}

type InputItem interface {
	// GetInput return user input in this act
	GetInput() string
	// GetAction will return what operator you used in this item
	GetAction() string
	// GetSub return a list of InputItem, it should only return when no action value
	GetSub() []InputItem
}

// engine 实现了基础的执行引擎，可以进行顺序的执行的操作
type engine struct {
	sync.Once
	init   bool
	actors map[string]Operator
}

// checkInit use double check to do init once
func (e *engine) checkInit() {
	if !e.init {
		e.Do(e.Init)
	}
}

// Init will make a new map of actors if actors is nil and set init to true
func (e *engine) Init() {
	if e.actors == nil {
		e.actors = make(map[string]Operator)
	}
	e.init = true
}

// RegisterCheck check if act key had been registered and return error
func (e *engine) RegisterCheck(key string) error {
	e.checkInit()
	if _, ok := e.actors[key]; ok {
		return errors.New(fmt.Sprintf("%s had been registerd in action", key))
	}
	return nil
}

// RegisterForAction register act key and operator into actors map
func (e *engine) RegisterForAction(act string, operator Operator) error {
	if act == "" || operator == nil {
		return errors.New("Invalid action operator ")
	}
	if err := e.RegisterCheck(act); err != nil {
		return err
	}
	e.actors[act] = operator
	return nil
}

// CheckInputs check all act had been registered (检查所有操作都是已注册)
func (e *engine) CheckInputs(flows []InputItem) error {
	for _, flow := range flows {
		if act := flow.GetAction(); act != "" {
			if _, had := e.actors[act]; !had {
				return errors.New("unregistered act " + act)
			}
		} else if fls := flow.GetSub(); len(fls) > 0 {
			if err := e.CheckInputs(fls); err != nil {
				return err
			}
		} else {
			return errors.New("invalid flow " + flow.GetInput())
		}
	}
	return nil
}

// Run check condition and run by follows InputItem
func (e *engine) Run(ctx context.Context, in interface{}, flows []InputItem) (out interface{}, err error) {
	e.checkInit()
	if err = e.CheckInputs(flows); err != nil {
		return in, err
	}
	return e.run(ctx, in, flows)
}

// run is no check run function
func (e *engine) run(ctx context.Context, in interface{}, flows []InputItem) (out interface{}, err error) {
	pre := in
	var result interface{}
	for _, flow := range flows {
		if act := flow.GetAction(); act != "" {
			if f, ok := e.actors[act]; ok && f != nil {
				result, err = f.Process(ctx, pre, flow.GetInput())
				if err != nil {
					return pre, err
				}
			}
		} else if fls := flow.GetSub(); len(fls) > 0 {
			result, err = e.run(ctx, pre, fls)
			if err != nil {
				return pre, err
			}
		} else {
			// 如果无效视为结果就是输出
			result = pre
		}
		// 结果缓存为下一次的输入
		pre = result
	}
	return result, nil
}

// NewEngine return and implement of Engine
func NewEngine() Engine {
	e := &engine{}
	e.Init()
	return e
}
